//
//  DatabaseProvider.m
//  objectiverecord
//
//  Created by Gang.Wang on 12-5-18.
//  Copyright (c) 2012年 yuike. All rights reserved.
//

#import "DatabaseProvider.h"
#import "header.h"

@interface DatabaseProvider ()
{
    NSObject * lock;
}
@end

@implementation DatabaseProvider

static int MAX_RETRY_NUMBER = 10;

- (id)initWithPath:(NSString *)path {
  self = [super init];
  if(self) {
    dbPath = [path copy];
    db  = nil;
      
      lock = [[NSObject alloc] init];
//    self->dbLock = [[NSLock alloc] init];
  }
  return self;
}

- (void)finalize 
{
  [self close];
  [super finalize];
}

- (void)dealloc 
{
  [self close];
    
 YRelease(dbPath)
    
  YRelease(self->dbLock)
    YRelease(lock)
  [super dealloc];
}

- (NSString *)sqliteVersion {
  return [NSString stringWithFormat:@"%s", sqlite3_libversion()];
}

- (BOOL) dbIsNil
{
    return db ? FALSE : TRUE ;
}
- (BOOL)open 
{  
    @synchronized(lock)
    {
        [self->dbLock lock];
        if(db) 
        {
            statement = nil;
            [self->dbLock unlock];
            return YES;
        }
        int error = sqlite3_open((dbPath ? [dbPath fileSystemRepresentation] : ":memory:"), &db );
        if(error != SQLITE_OK) {
            DebugLog(@"Database opening error is : %d", error);
            [self->dbLock unlock];
            return NO;
        }
        
        sqlite3_config(SQLITE_CONFIG_MULTITHREAD);
        statement = nil;
        [self->dbLock unlock];
        return YES; 
    }
}

- (BOOL)close {
    @synchronized(lock)
    {
        [self->dbLock lock];
        if (!db) {
            [self->dbLock unlock];
            return YES;
        }
        
        BOOL retry;
        int  retryCount = 0;
        
        do {
            retry   = NO;
            int status      = sqlite3_close(db);
            if (SQLITE_BUSY == status || SQLITE_LOCKED == status) {
                retry = YES;
                usleep(20);
                if (retryCount++ > MAX_RETRY_NUMBER) {
                    DebugLog(@"Database unable to close");
                    [self->dbLock unlock];
                    return NO;
                }
            }
            else if (SQLITE_OK != status) {
                DebugLog(@"Database can't close(fatal error): %d", status);
            }
        }
        while (retry);
        
        
        db = nil;
        [self->dbLock unlock];
        return YES;
    }    
}

- (id)executeQuery:(NSString *)sql withArgument:(id)arg {
    @synchronized(lock)
    {
        [self->dbLock lock];
        NSInteger lastRowId = 0;
        NSInteger res = 0;
        
        BOOL retry;
        int  retryCount = 0;
        
        if (!statement) {
            do {
                retry = NO;
                if (!db) {
                    DebugLog(@"db is nil");
                }
                int status = sqlite3_prepare_v2(db, [sql UTF8String], -1, &statement, NULL);
                if (SQLITE_BUSY == status || SQLITE_LOCKED == status) {
                    retry = YES;
                    usleep(20);
                    if (retryCount++ > MAX_RETRY_NUMBER) {
                        DebugLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, dbPath);
                        sqlite3_finalize(statement);
                        
                        [self->dbLock unlock];
                        return nil;
                    }
                }else if (SQLITE_OK != status) {
                    DebugLog(@"Database can't execute(fatal error): %d  sql:%@", status, sql);
                    sqlite3_finalize(statement);
                    
                    [self->dbLock unlock];
                    return nil;
                }
            } while (retry); 
        }
        
        sqlite3_reset(statement);
        int resultCode = -1;
        NSMutableArray *selectArray = [[[NSMutableArray alloc]init]autorelease];
        
        do { 
            retry = NO;
            resultCode = sqlite3_step(statement);
            
            if (SQLITE_BUSY == resultCode || SQLITE_LOCKED == resultCode) {
                retry = YES;
                usleep(20);
                if (retryCount++ > MAX_RETRY_NUMBER) {
                    DebugLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, dbPath);
                    DebugLog(@"Database busy");
                    sqlite3_finalize(statement);
                    
                    [self->dbLock unlock];
                    return nil;
                }
            } else if (resultCode == SQLITE_DONE) {
                // create 
                if([[sql substringToIndex:1] isEqualToString:@"I"]) {
                    lastRowId = sqlite3_last_insert_rowid(db);
                    sqlite3_finalize(statement);
                    statement = nil;
                    NSNumber *ns_lastRowId = [[[NSNumber alloc] initWithLong:lastRowId] autorelease];
                    
                    [self->dbLock unlock];
                    return ns_lastRowId;
                    // update or delete
                } else if ([[sql substringToIndex:1] isEqualToString:@"U"] || 
                           [[sql substringToIndex:1] isEqualToString:@"D"] ) {
                    sqlite3_finalize(statement);
                    statement = nil;
                    
                    [self->dbLock unlock];
                    return nil;
                }
            } else if(resultCode == SQLITE_ROW)    {
                // sqlite3_step() has another row ready 
                // check if a table exists
                if ([arg isKindOfClass:[NSString class]] && [arg isEqualToString:@"0x00"]) {
                    res = sqlite3_column_int64(statement, 0);
                    sqlite3_finalize(statement);
                    statement = nil;
                    NSNumber *ns_result = [[[NSNumber alloc] initWithLong:res] autorelease];
                    
                    [self->dbLock unlock];
                    return ns_result;
                }
                else if ([arg isKindOfClass:[NSArray class]]) {
                    NSMutableDictionary *selectDic  = [NSMutableDictionary dictionary];
                    for (int i=0; i<[arg count]; i++) {
                        NSString * value = [NSString stringWithString:@""];    
                        const char* valueChar = (const char*) sqlite3_column_text(statement,i);
                        if (valueChar) 
                        {
                            value = [NSString stringWithCString:valueChar encoding:NSUTF8StringEncoding];
                        }                                   
                        [selectDic setObject:value forKey:[arg objectAtIndex:i]];
                    }
                    [selectArray addObject:selectDic];
                    selectDic = nil;
                }
            } else {
                DebugLog(@"Unknown error calling sqlite3_step (%d: %s) ", resultCode, sqlite3_errmsg(db));
                DebugLog(@"DB Query: %@", sql);
            }
        } while (retry || resultCode == SQLITE_ROW);
        
        sqlite3_finalize(statement);
        statement = nil;
        
        [self->dbLock unlock];
        return selectArray;
    }    
}

@end
